package com.run_java_test.jar_upload_run.services.v1;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 继承自 URLClassLoader，这是 Java 提供的一种 ClassLoader，
 * 支持从 URL 指定的资源（如 JAR 文件或目录）中加载类和资源。
 * 扩展 URLClassLoader 的功能，增加了加载特定包中的所有类的能力。
 */

public class ProjectJarClassLoader extends URLClassLoader {

    /**
     * 用于存储成功加载的类的列表。
     * 这些类是从 JAR 文件中加载，并且满足给定的包名前缀。
     */
    private final static List<Class<?>> loadedClasses = new ArrayList<>();

    /**
     * 构造一个新的 ProjectJarClassLoader 实例。
     * 参数：
     * URL jarUrl：表示 JAR 文件的路径。
     * 调用父类构造器：
     * new URL[]{jarUrl}：将 JAR 文件的 URL 传递给 URLClassLoader。
     * Thread.currentThread().getContextClassLoader()：将当前线程的上下文类加载器作为父加载器。
     *
     * @param jarUrl
     */
    public ProjectJarClassLoader(URL jarUrl) {
        super(new URL[]{jarUrl}, Thread.currentThread().getContextClassLoader());
    }

    /**
     * 功能：
     * 从 JAR 文件中加载所有以 packagePrefix 开头的类。
     * 返回成功加载的类列表。
     * 参数：
     * String packagePrefix：包名的前缀，例如 "com.example"。
     * 返回值：
     * 包含成功加载的类的列表。
     *
     * @param packagePrefix
     * @return
     * @throws IOException
     */
    public List<Class<?>> loadAllClasses(String packagePrefix) throws IOException {
        // 用于调用这个方法后, 直接可以返回jar中所有的 class文件
        List<Class<?>> classes = new ArrayList<>();
        // 打开 JAR 文件，getURLs()[0].getFile() 返回 JAR 文件的路径。 获取第一个jar路径的jar文件
        try (JarFile jarFile = new JarFile(getURLs()[0].getFile())) {
            // 遍历 JAR 文件中的所有条目（JarEntry），包括类文件、资源文件等。
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                // 获取包中的所有内容, 各类文件夹, 各类文件, 不管是配置文件 还是 class 文件
                JarEntry entry = entries.nextElement();
                if (entry.getName().endsWith(".class")) {
                    // 筛选以 .class 结尾的条目（即类文件）。
                    // 将类文件路径（如 com/example/MyClass.class）转换为类的全限定名（如 com.example.MyClass）。
                    String className = entry.getName()
                            .replace("/", ".")
                            .replace(".class", "");
                    // 检查类名是否以指定的 packagePrefix 开头。
                    // 使用 loadClass 方法加载类：
                    // 成功加载的类添加到 classes 和 loadedClasses。
                    // 捕获 ClassNotFoundException，以便处理加载失败的类。
                    if (className.startsWith(packagePrefix)) {
                        try {
                            // 加载class 文件
                            Class<?> clazz = super.loadClass(className);
                            // 将class文件记录
                            classes.add(clazz);
                            // 将class文件记录
                            ProjectJarClassLoader.loadedClasses.add(clazz);
                        } catch (ClassNotFoundException e) {
                            System.err.println("Failed to load class: " + className);
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        // 关闭 URLClassLoader 对jar包占用
        this.close();
        return classes;
    }

    // 返回记录的所有class 文件
    public static List<Class<?>> getLoadedClasses() {
        return ProjectJarClassLoader.loadedClasses;
    }
}
